import java.awt.List;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;

import com.wcohen.ss.expt.MatchData;
import com.wcohen.ss.expt.NGramBlocker;
import com.wcohen.ss.expt.TokenBlocker;

import uk.ac.shef.wit.simmetrics.similaritymetrics.InterfaceStringMetric;
import uk.ac.shef.wit.simmetrics.similaritymetrics.JaccardSimilarity;
import uk.ac.shef.wit.simmetrics.similaritymetrics.Jaro;
import uk.ac.shef.wit.simmetrics.similaritymetrics.JaroWinkler;
import uk.ac.shef.wit.simmetrics.similaritymetrics.Levenshtein;

public class Main {
	public static int matches = 0;
	public static String usedMetric = null;
	
	public static void main(String[] args) throws IOException {		
//		// Code for reading with the old reader method
//		ArrayList<Entity>[] data;
//		ArrayList<Entity> list1 = new ArrayList<Entity>();
//		ArrayList<Entity> list2 = new ArrayList<Entity>();
//		ArrayList<Similarity> result = new ArrayList<Similarity>();
//		ArrayList<Similarity> eval = new ArrayList<Similarity>();
//
//		Reader reader = new Reader();
//		data = reader.readFile();
//
//		list1 = data[0];
//		list2 = data[1];
//		result = getSimilarities(list1,list2,Sims.JARO);
//		eval = evaluate(result, 10);
//		for(Similarity sim : eval){
//			System.out.println(sim.getPrecision() + " " + sim.getRecall());
//		}
		
		// Code below uses the MatchData structure so the blocker from that package can be used
//		ArrayList<Entity> list1 = new ArrayList<Entity>();
//		ArrayList<Entity> list2 = new ArrayList<Entity>();
//		ArrayList<Similarity> result = new ArrayList<Similarity>();
//		ArrayList<Similarity> eval = new ArrayList<Similarity>();
		MatchData[] data;
//		NGramBlocker blockedData = new NGramBlocker();
//		blockedData.setMinNGramSize(4);
		
		MatchReader reader = new MatchReader();
		data = reader.readAllFiles();

//		// Creating one big list to be evaluated after blocking all the lists
//		for(int i = 0; i < data.length; i++){
//			blockedData.block(data[i]);
//			for(int j = 0; j < blockedData.size(); j++){
//				String[] ent1;
//				String[] ent2;
//				ent1 = blockedData.getPair(j).getA().toString().split("'");
//				ent2 = blockedData.getPair(j).getB().toString().split("'");
//				list1.add(new Entity(ent1[1], ent1[3], blockedData.getPair(j).getA().unwrap()));
//				list2.add(new Entity(ent2[1], ent2[3], blockedData.getPair(j).getB().unwrap()));
//				if(blockedData.getPair(j).isCorrect()){
//					matches++;
//				}
//				if(!ent1[4].equals(" unwrapped: ")){
//					System.out.println("");
//				}
//			}
//		}
		
//		for(Sims flag : Sims.values()){
			double[] avgResult;
			avgResult = evaluateAvg(data, Sims.LCSSIMILARITY);

			System.out.println(usedMetric);
			for(int i = 0; i < avgResult.length; i++){
				System.out.println(String.format("%.3f", avgResult[i]));
			}
//		}
		
//		result = getSimilarities(list1,list2,Sims.JAROWINKLER);
		
//		// Code for checking entries in the whole list
//		for(Similarity sim : result){
//			if(sim.getFirstEnt().getIdentifier().equals("Conference_wwwWeb_Site") && sim.getSecondEnt().getIdentifier().equals("Conference_volumeConference")){
//				System.out.println();
//			}
//		}
		
//		eval = evaluate(result, 10);
//		for(int i = 0; i < eval.size(); i++){
//			System.out.println("Minimal recall: " + (double)i/(eval.size()-1) + " - " + eval.get(i).getPrecision() + " " + eval.get(i).getRecall());
//		}
		
//		LCSSimilarity test = new LCSSimilarity();
//		System.out.println(test.getSimilarity("test", "testte"));
	}
	
	public static ArrayList<Similarity> getSimilarities(ArrayList<Entity> list1, ArrayList<Entity> list2, Sims flag){
		ArrayList<Similarity> result = new ArrayList<Similarity>();
		InterfaceStringMetric simMetric = null;
		int matches = 0;
		int foundMatches = 0;

		if(flag == Sims.JARO){
			simMetric = new Jaro();
			usedMetric = simMetric.getShortDescriptionString();
		}
		
		if(flag == Sims.JAROWINKLER){
			simMetric = new JaroWinkler();
			usedMetric = simMetric.getShortDescriptionString();
		}
		
		if(flag == Sims.LEVENSHTEIN){
			simMetric = new Levenshtein();
			usedMetric = simMetric.getShortDescriptionString();
		}
		
		if(flag == Sims.JACCARD){
			simMetric = new JaccardSimilarity();
			usedMetric = simMetric.getShortDescriptionString();
		}
		
		if(flag == Sims.SOFTTFIDF){
			simMetric = new SoftTFIDFAdapter();
			usedMetric = simMetric.getShortDescriptionString();
		}
		
		if(flag == Sims.LCSSIMILARITY){
			simMetric = new LCSSimilarity();
			usedMetric = simMetric.getShortDescriptionString();
		}
		
//		if(flag == Sims.WINKLERSIMILARITY){
//			simMetric = new WinklerSimilarity();
//			usedMetric = simMetric.getShortDescriptionString();
//		}
		
//		for(Entity ent1 : list1){
//			for(Entity ent2 : list2){
//				Similarity temp = new Similarity(ent1,ent2,simMetric.getSimilarity(ent1.getName(),ent2.getName()));
//				result.add(temp);
////				System.out.println(temp);
//				if(temp.getMatch() == true){
//					matches++;
//				}
//			}
//		}
		
		for(int i = 0; i < list1.size(); i++){
			Entity ent1 = list1.get(i);
			Entity ent2 = list2.get(i);
			Similarity temp = new Similarity(ent1,ent2,simMetric.getSimilarity(ent1.getName(),ent2.getName()));
			if(temp.getMatch() == true){
				matches++;
			}
			result.add(temp);
		}
		Collections.sort(result);
		
		for(int i = 1; i <= result.size(); i++){
			Similarity temp = result.get(i-1);
			if(temp.getMatch() == true){
				foundMatches++;
			}
			double recall = (double)foundMatches/matches;
			double precision = (double)foundMatches/i;
			temp.setRecall(recall);
			temp.setPrecision(precision);
			result.set(i-1,temp);
		}
//		System.out.println(result.size());
//		System.out.println(matches);
//		System.out.println(foundMatches);
		return result;
	}
	
	public static ArrayList<Similarity> evaluate(ArrayList<Similarity> list, int intervals){
		ArrayList<Similarity> result = new ArrayList<Similarity>();
		Similarity max = list.get(list.size()-1);
		int foo = list.size()-1;
		int temp2 = 0;
		for(double i = intervals; i >= 0; i--){
			double temp = i/intervals;
//			System.out.println(foo);
//			System.out.println(temp);
			for(int j = foo; j >= 0 && list.get(j).getRecall() >= temp; j--){
				if(max.getPrecision() < list.get(j).getPrecision()){
					max = list.get(j);
//					System.out.println(j);
				}
				temp2++;
			}
			result.add(0, max);
			foo = (list.size()-1)-temp2;
		}
		return result;
	}
	
	public static double[] evaluateAvg(MatchData[] data, Sims flag){
		NGramBlocker blockedData = new NGramBlocker();
		blockedData.setMinNGramSize(4);
//		TokenBlocker blockedData = new TokenBlocker();
		ArrayList<Similarity>[] results = new ArrayList[data.length];
		for(int i = 0; i < data.length; i++){
			blockedData.block(data[i]);
			ArrayList<Entity> list1 = new ArrayList<Entity>();
			ArrayList<Entity> list2 = new ArrayList<Entity>();
			ArrayList<Similarity> temp = new ArrayList<Similarity>();
			ArrayList<Similarity> tempEval = new ArrayList<Similarity>();
			for(int j = 0; j < blockedData.size(); j++){
				String[] ent1;
				String[] ent2;
				ent1 = blockedData.getPair(j).getA().toString().split("'");
				ent2 = blockedData.getPair(j).getB().toString().split("'");
				list1.add(new Entity(ent1[1], ent1[3], blockedData.getPair(j).getA().unwrap()));
				list2.add(new Entity(ent2[1], ent2[3], blockedData.getPair(j).getB().unwrap()));
				if(blockedData.getPair(j).isCorrect()){
					matches++;
				}
				// A check to see weather the data is read correctly
				if(!ent1[4].equals(" unwrapped: ")){
					System.out.println("");
				}
			}
//			System.out.println(list1.size());
			temp = getSimilarities(list1, list2, flag);
			tempEval = evaluate(temp, 10);
			results[i] = tempEval;
		}
		double[] avgResult = new double[results[0].size()];
		for(int i = 0; i < results[0].size(); i++){
			double total = 0;
			for(int j = 0; j < results.length; j++){
				total = total + results[j].get(i).getPrecision();
			}
			double avg = total/results.length;
			avgResult[i] = avg;
		}
		return avgResult;
	}
}
